package com.pig4cloud.pigx.contract.thread.repair;

import com.pig4cloud.pigx.common.customize.enums.CommonDictEnum;
import com.pig4cloud.pigx.common.customize.mongo.utils.MongoUtils;
import com.pig4cloud.pigx.common.customize.utils.id.IdWorker;
import com.pig4cloud.pigx.contract.entity.ContractKLine;
import com.pig4cloud.pigx.contract.mapper.ContractKLineMapper;
import com.pig4cloud.pigx.contract.mongo.entity.ContractKLineMongo;
import com.pig4cloud.pigx.contract.utils.HttpUtils;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;

/**
 * 修复K线
 *
 */
@Component
@EnableScheduling
@AllArgsConstructor
public class RepairKLine {

    private final IdWorker idWorker;

    private final MongoTemplate mongo;

    private final ContractKLineMapper lineMapper;

    private final static int LIMIT = 1000;

    /**
     * 异常K线修复(补)
     *
     * @param symbolId       币对id
     * @param symbolName     币对名称
     * @param startTime      开始时间戳
     * @param endTime        结束时间戳
     * @param interval       K线类型
     * @param collectionName mongo表名
     * @param tableName      mysql表名
     */
    public ContractKLineMongo repairForInsert(
            @NotNull Long symbolId,
            @NotNull String symbolName,
            @NotNull String interval,
            Long startTime,
            Long endTime,
            @NotNull String collectionName,
            @NotNull String tableName
    ) {
        List<List<Object>> list = new ArrayList<>();
        // 由于币安一次最多支持读取1000条数据的原因,采用循环读取,直至读完。
        while (true) {
            MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
            // 拼币安查询K线接口的url
            String url = getUrl(symbolName, interval, LIMIT, startTime, endTime);
            // 发送http请求获取币安K线数据
            List<List<Object>> request = (List<List<Object>>) HttpUtils.sendGetRequest(url, params);
            list.addAll(request);
            if (request.size() < LIMIT) {
                break;
            }
            startTime = Long.parseLong(request.get(LIMIT - 1).get(6).toString());
        }
        // 循环读取数据 开始修复
        try {
            for (List<Object> objects : list) {
                try {
                    // 组装mongo插入数据模型
                    ContractKLineMongo kLineMongo = getContractKLineMongo(objects, symbolId, symbolName);
                    // mongo补充
                    mongo.insert(kLineMongo, collectionName);
                    ContractKLine kLine = new ContractKLine();
                    BeanUtils.copyProperties(kLineMongo, kLine);
                    // 表名
                    kLine.setTableName(tableName);
                    // mysql补充
                    lineMapper.insertContractKLine(kLine);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return getContractKLineMongo(list.get(0), symbolId, symbolName);
    }

    /**
     * 异常K线修复(修)
     *
     * @param symbolId       币对id
     * @param symbolName     币对名称
     * @param interval       K线类型
     * @param startTime      开始时间戳
     * @param collectionName mongo表名
     */
    public ContractKLineMongo repairForUpdate(
            @NotNull Long symbolId,
            @NotNull String symbolName,
            @NotNull String interval,
            @NotNull Long startTime,
            @NotNull String collectionName
    ) {
        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
        // 拼币安查询K线接口的url
        String url = getUrl(symbolName, interval, 1, startTime, null);
        // 发送http请求获取币安K线数据
        List<List<Object>> request = (List<List<Object>>) HttpUtils.sendGetRequest(url, params);
        if (request.size() > 0) {
            ContractKLineMongo lineMongo = getContractKLineMongo(request.get(0), symbolId, symbolName);
            // 修改条件
            Query query = Query.query(Criteria.where("startTime").is(lineMongo.getStartTime()));
            // 修改内容
            Update update = MongoUtils.getUpdate(lineMongo);
            mongo.updateMulti(query, update, collectionName);
            return lineMongo;
        }
        return null;
    }

    public ContractKLineMongo getContractKLineMongo(List<Object> objects, Long symbolId, String symbolName) {
        // 组装参数
        ContractKLineMongo kLineMongo = new ContractKLineMongo();
        kLineMongo.setId(idWorker.nextId());
        kLineMongo.setSymbolId(symbolId);
        kLineMongo.setSymbolName(symbolName);
        // 开盘时间
        long openTime = Long.parseLong(objects.get(0).toString());
        kLineMongo.setStartTime(toLocalDateTime(openTime));
        // 开盘价
        String openPrice = objects.get(1).toString();
        BigDecimal bigDecimal = new BigDecimal(openPrice);
        kLineMongo.setOpenPrice(bigDecimal);
        // 最高价
        String highPrice = objects.get(2).toString();
        kLineMongo.setHighPrice(new BigDecimal(highPrice));
        // 最低价
        String lowPrice = objects.get(3).toString();
        kLineMongo.setLowPrice(new BigDecimal(lowPrice));
        // 收盘价
        String closePrice = objects.get(4).toString();
        kLineMongo.setClosePrice(new BigDecimal(closePrice));
        // 币数量
        String num = objects.get(5).toString();
        kLineMongo.setDealNum(new BigDecimal(num));
        // 收盘时间
        long closeTime = Long.parseLong(objects.get(6).toString()) + 1;
        kLineMongo.setEndTime(toLocalDateTime(closeTime));
        // 总价值(U)
        String price = objects.get(7).toString();
        kLineMongo.setDealPrice(new BigDecimal(price));
        kLineMongo.setCreateBy(1L);
        kLineMongo.setCreateTime(LocalDateTime.now());
        kLineMongo.setUpdateBy(kLineMongo.getCreateBy());
        kLineMongo.setUpdateTime(kLineMongo.getCreateTime());
        kLineMongo.setIsDeleted(CommonDictEnum.IsDeleted.NO.getValue());
        return kLineMongo;
    }

    public static String getUrl(
            String symbolName,
            String interval,
            int limit,
            Long startTime,
            Long endTime
    ) {
        String url = "https://fapi.binance.com/fapi/v1/klines";
        url += "?symbol=" + symbolName;
        url += "&interval=" + interval;
        url += "&limit=" + limit;
        if (startTime != null) {
            url += "&startTime=" + startTime;
        }
        if (endTime != null) {
            url += "&endTime=" + endTime;
        }
        return url;
    }

    /**
     * 时间戳转LocalDateTime
     *
     * @param timeMillis 时间戳
     * @return 时间(LocalDateTime)
     */
    public static LocalDateTime toLocalDateTime(long timeMillis) {
        ZoneId zoneId = ZoneId.systemDefault();
        return Instant.ofEpochMilli(timeMillis).atZone(zoneId).toLocalDateTime();
    }

    /**
     * LocalDateTime转时间戳
     *
     * @param localDateTime 时间(LocalDateTime)
     * @return 时间戳
     */
    public static long toTimeStamp(LocalDateTime localDateTime) {
        ZoneId zoneId = ZoneId.systemDefault();
        return localDateTime.atZone(zoneId).toInstant().toEpochMilli();
    }


}
